package com.example.netty.io.client

import com.example.netty.io.core.EventType
import com.example.netty.io.core.IMessageListener
import com.example.netty.io.core.NettyConfigure
import com.example.netty.io.core.NettyLog
import com.example.netty.io.core.NioSocketTask
import io.netty.bootstrap.Bootstrap
import io.netty.channel.ChannelFuture
import io.netty.channel.ChannelFutureListener
import io.netty.channel.ChannelOption
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.nio.NioSocketChannel
import java.io.IOException
import java.net.SocketAddress
import java.util.concurrent.TimeUnit


class NettyClientTask(
    private val address: SocketAddress,
    private val messageListener: IMessageListener
) : NioSocketTask() {
    companion object {
        private const val TAG = "NettyClientTask"
    }

    //事件循环组
    private var bootstrap = Bootstrap()

    //线程池
    private var loopGroup = NioEventLoopGroup()

    private val handler = ClientChannelHandler(messageListener)

    init {
        reset()
    }

    private fun reset() {
        retryTimes = 0
        bootstrap = Bootstrap()
        loopGroup = NioEventLoopGroup()
        bootstrap.group(loopGroup)
            .channel(NioSocketChannel::class.java)
            .handler(handler)
            .option(ChannelOption.TCP_NODELAY, true)
            .option(ChannelOption.SO_KEEPALIVE, true)
            .option(ChannelOption.SO_TIMEOUT, NettyConfigure.soTimeOut)
    }

    override fun run() {
        if (isRelease.compareAndSet(true, false)) {
            reset()
        }
        retryTimes++
        NettyLog.d(TAG, "start Connect times=$retryTimes thread=${Thread.currentThread().name}")
        try {
            val channelFuture = bootstrap.connect(address)
            channelFuture.addListener(object : ChannelFutureListener {
                override fun operationComplete(future: ChannelFuture) {
                    if (future.isSuccess) {
                        onSuccess(future.channel())
                        NettyLog.i(TAG, "Client connect success ${Thread.currentThread().name}")
                    } else {
                        if (canRetry()) {
                            future.channel().eventLoop().schedule({
                                NettyLog.w(TAG, "重连服务器.....", channelFuture.cause())
                                run()
                                future.channel().close()
                            }, 3000, TimeUnit.MILLISECONDS)
                        } else {
                            release()
                            messageListener.onEventArrived(future.channel(), EventType.DISCONNECTED)
                        }
                    }
                }
            })
        } catch (e: IOException) {
            close()
            NettyLog.e(TAG, "client error", e)
        }
    }

    override fun release() {
        try {
            close()
            if (isRelease.compareAndSet(false, true)) {
                loopGroup.shutdownGracefully()
            }
            NettyLog.e(TAG, "client close")
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    fun sendData(msg: String) {
        channel?.let { ch ->
            if (ch.isActive) {
                ch.writeAndFlush(msg)
            }
        }
    }
}
